<script setup>
import { useTable } from '@/hooks/useTable'
import { useSelection } from '@/hooks/useSelection'
import { onMounted, reactive, ref, useSlots, watch } from 'vue'
import TableColumn from './components/TableColumn.vue'
import Pagination from './components/Pagination.vue'
import SearchForm from './components/SearchForm/index.vue'
import ColSetting from './components/ColSetting.vue'
import printJS from 'print-js'
const props = defineProps({
  columns: {
    // 列配置项
    type: Array,
    default: () => []
  },
  toolButton: {
    // 是否显示表格功能按钮
    type: Boolean,
    default: true
  },
  requestApi: Function, // 请求表格数据的api,
  initParam: {
    // 初始化请求参数  这个参数将会随着每一次列表请求一同作为参数发出
    default: () => {}
  },
  pagination: {
    // 是否需要分页组件
    type: Boolean,
    default: true
  },
  dataCallback: Function, // 返回数据的回调函数，可以对数据进行处理
  selectId: {
    // 多选情况下指定的字段名,最终返回
    type: String,
    default: 'id'
  }
})
const tableColumns = ref(props.columns)

// 定义 enumMap 存储 enum 值
const enumMap = ref(new Map())

// 处理columns数据
const flatColumnsFunc = (columns) => {
  const generateData = []
  columns.forEach(async col => {
    const newData = reactive(col)
    // 给每一项 column 添加 isShow && isFilterEnum 属性
    newData.isShow = ref(newData.isShow ?? true)
    newData.isFilterEnum = ref(newData.isFilterEnum ?? true)

    generateData.push(col)
  })
  return generateData
}

// 监听传入的columns变化  获取更新遍历的对象
watch(() => props.columns, newVal => {
  newVal.forEach(async col => {
    if(col.enum){
      if(typeof col.enum !== 'function') {
        enumMap.value.set(col.prop, col.enum)
      } else {
        const data = await col.enum()
        enumMap.value.set(col.prop, data)
      }
      enumMap.value = new Map(enumMap.value)
    }
  })
}, { deep: true })

// 扁平 columns
const flatColumns = ref()
flatColumns.value = flatColumnsFunc(tableColumns.value)

// 表格多选 Hooks
const { selectionChange, getRowKeys, selectedList, selectedListIds, isSelected } = useSelection(props.selectId)

// 表格操作 Hooks
const {
  tableData,
  pageable,
  searchParam,
  searchInitParam,
  getTableList,
  search,
  reset,
  handleSizeChange,
  handleCurrentChange
} = useTable(props.requestApi, props.initParam, props.pagination, props.dataCallback)

const slots = useSlots()

const tableRef = ref(null)

// 是否显示搜索模块
const isShowSearch = ref(true)
const showSearch = () => {
  isShowSearch.value = !isShowSearch.value
}
// 过滤需要搜索的配置项 && 处理搜索排序
// 列配置项包含search对象包含el属性则是搜索项
// order配置顺序
const searchColumns = tableColumns.value
  .filter(item => item.search?.el)
  .sort((a, b) => (b.search?.order ?? 0) - (a.search?.order ?? 0))

// 设置搜索表单的默认值
searchColumns.forEach((column) => {
  if (column.search?.defaultValue !== undefined && column.search?.defaultValue !== null)
    searchInitParam.value[column.search.key || column.prop] = column.search?.defaultValue
})

// 列设置 ==> 过滤掉不需要设置显隐的列
const colRef = ref(null)
const colSetting = tableColumns.value?.filter((item) => {
  return item.isShow && !['selection', 'index', 'operation', 'expand'].includes(item.type)
})
const openColSetting = () => {
  colRef.value.openColSetting()
}

const searchFormRef = ref(null)
const searchFormHeight = ref(0)
onMounted(() => {
  searchFormHeight.value = searchFormRef.value.$el.offsetHeight
})
const SFHeight = ref(0)
const getElemHeight = (height) => {
  SFHeight.value = height
}
const cardTableHeight = ref(`calc(100% - ${isShowSearch.value ? SFHeight.value : 0}px - ${isShowSearch.value ? 36 : 24}px)`)
watch([isShowSearch, SFHeight], (newVal) => {
  cardTableHeight.value = `calc(100% - ${newVal[0] ? newVal[1] : 0}px - ${newVal[0] ? 36 : 24}px)`
})

const colSettingChange = () => {
  tableRef.value.doLayout()
}

// 处理打印数据 不打印隐藏列，将远程数据获取出来
const printData = () => {
  return tableData.value.map(item => {
    const target = Object.assign({}, item)
    for (const key in item) {
      if (Object.hasOwnProperty.call(item, key)) {
        const element = item[key];
        if(enumMap.value.has(key)){
          const fieldNames = props.columns.find(col => col.prop === key)?.fieldNames
          const val = [...enumMap.value.get(key)]
          const label = val.find(val => val[fieldNames?.value || 'value'] == element)?.[fieldNames?.label || 'label']
          target[key] = label
        }
      }
    }
    return target
  })
}
const handlePrint = () => {
  printJS({
    printable: printData(),
    header: props.title && `<div style="display: flex;flex-direction: column;text-align: center"><h2>${props.title}</h2></div>`,
    properties: tableColumns.value.filter(
      item =>
        item.isShow.value && item.type !== 'selection' && item.type !== 'index' && item.type !== 'expand' && item.prop !== 'operation',
    )
      .map((item) => {
        return {
          field: item.prop,
          displayName: item.label,
        }
      }),
    type: 'json',
    gridHeaderStyle:
			'border: 1px solid #ebeef5;height: 45px;font-size: 14px;color: #232425;text-align: center;background-color: #fafafa;',
    gridStyle: 'border: 1px solid #ebeef5;height: 40px;font-size: 14px;color: #494b4e;text-align: center',
  })
}

defineExpose({
  getTableList
})
</script>

<template>
  <div class="pro-table">
    <SearchForm
      ref="searchFormRef"
      v-show="isShowSearch"
      :columns="searchColumns"
      :search="search"
      :reset="reset"
      :searchParam="searchParam"
      :enum-map="enumMap"
      @getElemHeight="getElemHeight"
    />

    <!-- 表格内容 card -->
    <div class="card-table" :style="{height: cardTableHeight}">
      <!-- 表格头部 操作按钮 -->
      <div class="table-header">
        <div class="table-header-left">
          <slot
            name="tableHeader"
            :selected-list-ids="selectedListIds"
            :select-list="selectedList"
            :is-selected="isSelected"
          ></slot>
        </div>
        <div v-if="toolButton" class="header-button-ri">
          <el-button icon="el-icon-refresh" circle @click="getTableList" />
          <el-button v-if="columns.length" icon="el-icon-printer" circle @click="handlePrint" />
          <el-button v-if="columns.length" icon="el-icon-s-operation" circle @click="openColSetting" />
          <el-button v-if="searchColumns.length" icon="el-icon-search" circle @click="showSearch" />
        </div>
      </div>
      <el-table
        ref="tableRef"
        :data="tableData"
        v-bind="$attrs"
        :row-key="getRowKeys"
        height="100%"
        @selection-change="selectionChange"
      >
        <slot></slot>
        <div v-for="item in tableColumns" :key="JSON.stringify(item)">
          <!-- selection || index -->
          <el-table-column
            v-if="item.type === 'selection' || item.type === 'index'"
            v-bind="item"
            :align="item.align ?? 'center'"
            :reserve-selection="item.type === 'selection'"
          />
          <!-- <el-table-column v-if="item.type === 'expand'" v-slot="scope" v-bind="item" :align="item.align ?? 'center'">
            <component :is="item.render" v-if="item.render" :row="scope.row" />
            <slot v-else :name="item.type" :row="scope.row"></slot>
          </el-table-column> -->
          <!-- other 循环递归 -->
          <TableColumn v-if="!item.type && item.prop" v-bind="item" :enum-map="enumMap">
            <!-- 这里的$slots为通过调用当前组件传入的插槽 -->
            <template v-for="slotName in Object.keys(slots)" #[slotName]="scope">
              <slot :name="slotName" v-bind="scope" />
            </template>
          </TableColumn>
        </div>
        <!-- 无数据 -->
        <template #empty>
          <slot name="empty"></slot>
        </template>
      </el-table>
      <!-- 分页组件 -->
      <Pagination
        v-if="pagination"
        :pageable="pageable"
        :handle-size-change="handleSizeChange"
        :handle-current-change="handleCurrentChange"
      />
    </div>

    <ColSetting v-if="toolButton" ref="colRef" v-model="colSetting" @colSettingChange="colSettingChange" />
  </div>
</template>

<style lang="scss" scoped>
.pro-table{
  height: 100%;
}
.card-table {
  flex-shrink: 0;
  background-color: #fff;
  padding: $margin;
  display: flex;
  flex-direction: column;
  .table-header {
    margin-bottom: $margin;
    display: flex;
    justify-content: space-between;
  }
}

::v-deep.el-table__row .el-table__cell .cell{
  display: flex;
  align-items: center;
}
</style>
